# Copyright © 2025 kskou. All rights reserved.

# The software and associated documentation files (the "Software") provided in this tool are the property of kskou. Unauthorized reproduction, distribution, or modification of the Software, in whole or in part, is strictly prohibited. Any use of the Software must comply with applicable laws and regulations and obtain prior written permission from kskou.

# DISCLAIMER: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL kskou BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import requests
import json
import time

# 禁用 SSL 证书验证（仅用于测试环境）
requests.packages.urllib3.disable_warnings()

def read_config():
    try:
        with open('config.json', 'r') as f:
            # 过滤注释行
            content = ''.join(line for line in f if not line.strip().startswith(('//', '#')))
            return json.loads(content)
    except FileNotFoundError:
        print("配置文件 config.json 未找到，请检查。")
        return None
    except json.JSONDecodeError as e:
        print(f"配置文件格式错误: {e}")
        return None

def get_bios_settings_url(redfish_url, username, password):
    try:
        response = requests.get(f"{redfish_url}/Systems/1", auth=(username, password), verify=False)
        response.raise_for_status()
        system_data = response.json()
        bios_url = system_data.get("Bios", {}).get("@odata.id")
        if bios_url.startswith("/redfish/v1"):
            bios_url = bios_url[len("/redfish/v1"):]
        # 添加 /settings
        bios_url = f"{bios_url}/settings"
        return bios_url
    except requests.RequestException as e:
        print(f"请求出错: {e}，设备地址: {redfish_url}")
        return None

def modify_bios_settings(redfish_url, bios_url, username, password, settings):
    try:
        # 先获取 BIOS 设置资源，同时获取 ETag
        get_response = requests.get(f"{redfish_url}{bios_url}", auth=(username, password), verify=False)
        get_response.raise_for_status()
        etag = get_response.headers.get('ETag')

        headers = {
            "Content-Type": "application/json"
        }
        if etag:
            headers['If-Match'] = etag

        full_bios_url = f"{redfish_url}{bios_url}"
        response = requests.patch(full_bios_url, auth=(username, password), headers=headers,
                                  json=settings, verify=False)
        response.raise_for_status()
        print(f"BIOS 设置修改成功，设备地址: {redfish_url}")
        return True
    except requests.RequestException as e:
        print(f"修改 BIOS 设置失败: {e}，设备地址: {redfish_url}，响应内容: {response.text if 'response' in locals() else '无'}")
        return False

def get_system_reset_url(redfish_url, username, password):
    try:
        response = requests.get(f"{redfish_url}/Systems/1", auth=(username, password), verify=False)
        response.raise_for_status()
        system_data = response.json()
        
        actions = system_data.get('Actions', {})
        reset_action = actions.get('#ComputerSystem.Reset', {})
        reset_url = reset_action.get('target')
        
        if not reset_url:
            print(f"未找到系统重置操作，设备地址: {redfish_url}")
            return None
            
        # 规范化 URL，移除可能的 /redfish/v1 前缀
        if reset_url.startswith("/redfish/v1"):
            reset_url = reset_url[len("/redfish/v1"):]
            
        return reset_url
    except requests.RequestException as e:
        print(f"获取重置 URL 失败: {e}，设备地址: {redfish_url}")
        return None

def get_supported_reset_types(redfish_url, username, password):
    """获取服务器支持的重置类型列表"""
    try:
        response = requests.get(f"{redfish_url}/Systems/1", auth=(username, password), verify=False)
        response.raise_for_status()
        system_data = response.json()
        
        reset_action = system_data.get('Actions', {}).get('#ComputerSystem.Reset', {})
        
        # 尝试多种方式获取支持的重置类型
        reset_types = reset_action.get('ResetType@Redfish.AllowableValues', [])
        
        if not reset_types:
            # 某些服务器可能使用不同的字段名称
            reset_types = reset_action.get('ResetType@AllowableValues', [])
            
        if not reset_types:
            # 如果还是无法获取，返回常见的重置类型列表
            print("警告: 无法获取服务器支持的重置类型，使用默认列表")
            reset_types = [
                "On", "ForceOn", "GracefulShutdown", "ForceOff", 
                "GracefulRestart", "ForceRestart", "Nmi", "PowerCycle"
            ]
            
        return reset_types
    except Exception as e:
        print(f"获取支持的重置类型失败: {e}，使用默认列表")
        return [
            "ForceRestart", "On", "ForceOn", "GracefulShutdown", "ForceOff", 
            "GracefulRestart",  "Nmi", "PowerCycle"
        ]

def perform_system_reset(redfish_url, reset_url, username, password):
    """执行系统重置操作，尝试多种可能的重置类型格式"""
    # 获取支持的重置类型
    supported_types = get_supported_reset_types(redfish_url, username, password)
    print(f"服务器支持的重置类型: {supported_types}")
    
    # 定义尝试顺序，包含不同大小写和格式
    reset_types_to_try = [
        'ForceRestart','GracefulRestart', 'Graceful restart', 'gracefulRestart', 'GRACEFUL_RESTART',
        'ForceRestart', 'Force restart',  'FORCE_RESTART',
        'PowerCycle', 'Power cycle', 'powerCycle', 'POWER_CYCLE'
    ]
    
    # 按优先级选择可用的重置类型
    reset_type = None
    for t in reset_types_to_try:
        if not supported_types or t in supported_types:
            reset_type = t
            break
    
    if not reset_type:
        print(f"错误: 找不到合适的重置类型，支持的类型: {supported_types}")
        return False
    
    try:
        headers = {
            "Content-Type": "application/json"
        }
        
        reset_data = {
            "ResetType": reset_type
        }
        
        full_reset_url = f"{redfish_url}{reset_url}"
        print(f"尝试使用重置类型 '{reset_type}' 重启服务器")
        
        response = requests.post(full_reset_url, auth=(username, password), headers=headers,
                                json=reset_data, verify=False)
        response.raise_for_status()
        print(f"系统重启命令已发送，设备地址: {redfish_url}，重置类型: {reset_type}")
        
        # 可选：等待服务器重启
        print("等待服务器重启...")
        time.sleep(10)  # 等待10秒后尝试检查状态
        
        # 简单检查服务器是否可访问
        try:
            # 尝试访问根路径
            response = requests.get(redfish_url, auth=(username, password), verify=False, timeout=5)
            if response.status_code == 200:
                print("服务器已恢复响应")
            else:
                print(f"服务器响应异常: HTTP {response.status_code}")
        except requests.exceptions.RequestException:
            print("服务器仍在重启中...")
            
        return True
    except requests.RequestException as e:
        print(f"系统重启失败: {e}，设备地址: {redfish_url}")
        if 'response' in locals():
            try:
                error_data = response.json()
                print(f"错误详情: {error_data.get('error', {}).get('@Message.ExtendedInfo', [])}")
            except:
                print(f"响应内容: {response.text}")
        return False

def main():
    config = read_config()
    if config:
        for device in config:
            redfish_url = device['redfish_url']
            username = device['username']
            password = device['password']
            bios_settings = device['bios_settings']
            
            # 判断是否需要重启（新增配置项）
            need_restart = device.get('restart_after_config', False)

            bios_url = get_bios_settings_url(redfish_url, username, password)
            if bios_url:
                success = modify_bios_settings(redfish_url, bios_url, username, password, bios_settings)
                
                if success and need_restart:
                    print(f"准备重启设备: {redfish_url}")
                    reset_url = get_system_reset_url(redfish_url, username, password)
                    if reset_url:
                        perform_system_reset(redfish_url, reset_url, username, password)
                    else:
                        print(f"无法获取重启 URL，设备: {redfish_url}")

if __name__ == "__main__":
    main()